
#include <BOpenGL>
#include <BPainter>
#include "member_BXmlNode.h"
#include "member_BHtmlItem.h"
#include "member_BHtmlWidget.h"
#include "member_BObject.h"
#include "member_BTexture.h"
#include "IHtmlStyleReader.h"

using namespace BWE;

member_BHtmlItem::member_BHtmlItem(BHtmlItem* htmlItem)
{
	boss = htmlItem;
	html = 0;
	selected = false;
	display = BHtmlItem::Display_None;
	minSize.set(0, 0);
	maxSize.set(999999, 999999);
	floatAlign = Align_None;

	dirty = true;
	dirtyStyle = true;
	wordWrap = true;
	textAlign = Align_Left;
	inputIndex = 0;
	inputCount = 0;
	spacing = 3;
	lineHeight = 1;
	freshFunc = 0;
	paintFunc = 0;

	listStyle = BHtmlItem::Style_Disc;
	flexDirection = Orientation_None;
}
member_BHtmlItem::~member_BHtmlItem()
{

}

void member_BHtmlItem::attach(BHtmlWidget* htmlWidget)
{
	if (html == htmlWidget)
		return;
	html = htmlWidget;
	if (html)
	{
		dirty = true;
		selected = false;
		for (int i = 0; i < boss->childCount(); i++)
		{
			BHtmlItem* item = boss->child(i);
			item_member(item)->attach(html);
		}
	}
}
void member_BHtmlItem::detach(BHtmlWidget* htmlWidget)
{
	if (html != htmlWidget)
		return;
	if (html)
	{
		for (int i = 0; i < boss->childCount(); i++)
		{
			BHtmlItem* item = boss->child(i);
			item_member(item)->detach(html);
		}
		if (html_member(html)->selectedItems.contain(boss))
		{
			selected = false;
			html_member(html)->selectedItems.remove(boss);
			object_member(html)->emit(Signal_ItemSelected, boss);
		}
		html_member(html)->visualItems.remove(boss);
		if (html_member(html)->hoveredItem == boss)
		{
			html_member(html)->hoveredItem = 0;
			object_member(html)->emit(Signal_ItemHovered, 0);
		}
	}
	html = 0;
}

void member_BHtmlItem::dirtyHtml()
{
	if (html)
	{
		dirty = true;
		dirtyStyle = true;
		html_member(html)->dirty = true;
		html_member(html)->dirtyStyle = true;
		html_member(html)->dirtyResource = true;
		object_member(html)->emit(Signal_ItemChanged, boss);
		html->fresh();
	}
}
void member_BHtmlItem::resetStyle()
{
	realFont = 0;
	sizeRatio.reset();
	fixedSize.reset();
	background.reset();
	textColor.reset();
	freshFunc = 0;
	paintFunc = 0;
	display = BHtmlItem::Display_Inline;

	if (realStyle)
	{
		realStyle->clearValues();
		realStyle->clearSubs();
		realStyle->clearChildren();
	}

	const BString& name = boss->name();
	if (name.size() == 2 && name[0] == 'h' && name[1] >= '1' && name[1] <= '6')
	{
		display = BHtmlItem::Display_Block;
	}
	else if (name == "body")
	{
		display = BHtmlItem::Display_Block;
	}
	else if (name == "div")
	{
		display = BHtmlItem::Display_Block;
	}
	else if (name == "p")
	{
		display = BHtmlItem::Display_Block;
	}
	else if (name == "pre")
	{
		display = BHtmlItem::Display_Block;
	}
	else if (name == "span")
	{
		display = BHtmlItem::Display_Inline_Block;
	}
	else if (name == "table")
	{
		display = BHtmlItem::Display_Inline_Block;
		freshFunc = &member_BHtmlItem::fresh_table;
	}
	else if (name == "tr" || name == "li")
	{
		display = BHtmlItem::Display_Block;
	}
	else if (name == "td")
	{
		display = BHtmlItem::Display_Inline_Block;
	}
	else if (name == "ul" || name == "list")
	{
		display = BHtmlItem::Display_Inline_Block;
		freshFunc = &member_BHtmlItem::fresh_ulist;
		paintFunc = &member_BHtmlItem::paint_ulist;
	}
	else if (name == "ol")
	{
		display = BHtmlItem::Display_Inline_Block;
		freshFunc = &member_BHtmlItem::fresh_olist;
		paintFunc = &member_BHtmlItem::paint_olist;
	}
	else if (name == "dl")
	{
		display = BHtmlItem::Display_Inline_Block;
		freshFunc = &member_BHtmlItem::fresh_dlist;
		paintFunc = &member_BHtmlItem::paint_dlist;
	}
	else if (name == "img" || name == "image")
	{
		freshFunc = &member_BHtmlItem::fresh_image;
		paintFunc = &member_BHtmlItem::paint_image;
	}
}
void member_BHtmlItem::applyStyle(const BFont* upperFont)
{
	const BString& name = boss->name();

	const BString& styleText = boss->attrib("style");
	if (styleText.size())
	{
		if (realStyle.empty())
			realStyle = new IHtmlStyle();
		IHtmlStyleReader reader;
		reader.read(styleText, realStyle);
	}

	const BString& classText = boss->attrib("class");
	if (classText.size())
		classText.split(classes, ' ');

	const BString& displayText = boss->attrib("display");
	if (displayText.size())
		display = BWE::TextToDisplay(displayText);

	const BString& alignText = boss->attrib("align");
	if (alignText.size())
		BWE::StringToAlign(alignText, textAlign);

	const BString& spacingText = boss->attrib("spacing");
	if (spacingText.size())
		spacing = spacingText.toInt();

	const BString& widthText = boss->attrib("width");
	const BString& heightText = boss->attrib("height");
	if (image)
	{
		if (widthText.size())
		{
			if (widthText.endWith('%'))
				fixedSize.width() = int(widthText.toInt() / 100.0f * image->width());
			else
				fixedSize.width() = widthText.toInt();
		}

		if (heightText.size())
		{
			if (heightText.endWith('%'))
				fixedSize.height() = int(heightText.toInt() / 100.0f * image->height());
			else
				fixedSize.height() = heightText.toInt();
		}

		if (fixedSize.width() > 0 && fixedSize.height() == 0)
		{
			fixedSize.height() = int((fixedSize.width() / (float)image->width()) * image->height());
		}
		if (fixedSize.height() > 0 && fixedSize.width() == 0)
		{
			fixedSize.width() = int(fixedSize.height() / (float)image->height() * image->width());
		}
	}
	else
	{
		fixedSize.width() = widthText.toInt();
		fixedSize.height() = heightText.toInt();
	}

	if (realStyle)
	{
		realStyle->assign("background-color", background);
		realStyle->assign("text-align", textAlign);
		realStyle->assign("color", textColor);

		const BValue& widthValue = realStyle->value("width");
		if (widthValue.is<int>())
			fixedSize.width() = widthValue;
		else if (widthValue.is<float>())
			sizeRatio.width() = widthValue;

		const BValue& heightValue = realStyle->value("height");
		if (heightValue.is<int>())
			fixedSize.height() = heightValue;
		else if (heightValue.is<float>())
			sizeRatio.height() = heightValue;

		realStyle->assign("min-width", minSize.width());
		realStyle->assign("min-height", minSize.height());
		realStyle->assign("max-width", maxSize.width());
		realStyle->assign("max-height", maxSize.height());
		realStyle->assign("spacing", spacing);
		realStyle->assign("line-height", lineHeight);
		realStyle->assign("float", floatAlign);
		realStyle->assign("display", display);

		realStyle->assign("border-left-color", borderLeftColor);
		realStyle->assign("border-top-color", borderTopColor);
		realStyle->assign("border-right-color", borderRightColor);
		realStyle->assign("border-bottom-color", borderBottomColor);

		realStyle->assign("margin-left", margin.left());
		realStyle->assign("margin-top", margin.top());
		realStyle->assign("margin-right", margin.right());
		realStyle->assign("margin-bottom", margin.bottom());
		realStyle->assign("border-left-width", border.left());
		realStyle->assign("border-right-width", border.right());
		realStyle->assign("border-top-width", border.top());
		realStyle->assign("border-bottom-width", border.bottom());
		realStyle->assign("padding-left", padding.left());
		realStyle->assign("padding-right", padding.right());
		realStyle->assign("padding-top", padding.top());
		realStyle->assign("padding-bottom", padding.bottom());

		const BValue& fontValue = realStyle->value("font");
		if (fontValue.valid())
		{
			const BFont* basefont = fontValue;
			font = new BFont(*basefont);
		}

		const BValue& familyValue = realStyle->value("font-family");
		if (familyValue.valid())
		{
			BString family = familyValue;
			if (font.empty())
				font = new BFont(upperFont->size());
			font->setFamily(family);
		}

		if (font.empty())
			font = new BFont(*upperFont);

		const BValue& fontSizeValue = realStyle->value("font-size");
		if (fontSizeValue.valid())
		{
			int fontSize = font->size();
			if (fontSizeValue.is<int>())
			{
				fontSize = fontSizeValue;
			}
			else if (fontSizeValue.is<float>())
			{
				float ratio = fontSizeValue;
				fontSize = int(fontSize * ratio);
			}
			font->setSize(fontSize);
		}

		const BValue& positionValue = realStyle->value("position");
		if (positionValue.valid())
			position = (BString&)positionValue;

		const BValue& backgroundImageValue = realStyle->value("background-image");
		if (backgroundImageValue.valid())
			backgroundImage = (BImage*)backgroundImageValue;
		if (backgroundImage)
		{
			const BString& repeatText = realStyle->value("background-repeat");
			if (repeatText == "repeat")
				backgroundImage->setWrap(BTexture::Wrap_Repeat);
			else if (repeatText == "repeat-x")
				backgroundImage->setWrap(BTexture::Wrap_Repeat);
			else if (repeatText == "repeat-x")
				backgroundImage->setWrap(BTexture::Wrap_Repeat);
			else if (repeatText == "no-repeat")
				backgroundImage->setWrap(BTexture::Wrap_Clamp);
		}

		const BValue& listStyleTypeValue = realStyle->value("list-style-type");
		if (listStyleTypeValue.valid())
			listStyle = listStyleTypeValue;

		const BValue& listStyleImageValue = realStyle->value("list-style-image");
		if (listStyleImageValue.valid())
			listStyleImage = (BImage*)listStyleImageValue;

		applyRoundStyle();
	}
	if (display == BHtmlItem::Display_Block && floatAlign)
	{
		display = BHtmlItem::Display_Inline_Block;
	}
	if (display == BHtmlItem::Display_Flex)
	{
		realStyle->assign("flex-direction", flexDirection);
	}
	if (font)
		realFont = font;
	else
		realFont = upperFont;
	boss->emit(Signal_Styled, realStyle);
}
void member_BHtmlItem::applyRoundStyle()
{
	BSize radiusLeftTop;
	BSize radiusRightTop;
	BSize radiusLeftBottom;
	BSize radiusRightBottom;
	if (round)
	{
		radiusLeftTop = round->radiusLeftTop;
		radiusRightTop = round->radiusRightTop;
		radiusLeftBottom = round->radiusLeftBottom;
		radiusRightBottom = round->radiusRightBottom;
	}

	const BValue& topLeftRadiusValue = realStyle->value("border-top-left-radius");
	if (topLeftRadiusValue.valid())
		radiusLeftTop = topLeftRadiusValue;

	const BValue& topRightRadiusValue = realStyle->value("border-top-right-radius");
	if (topRightRadiusValue.valid())
		radiusRightTop = topRightRadiusValue;

	const BValue& bottomLeftRadiusValue = realStyle->value("border-bottom-left-radius");
	if (bottomLeftRadiusValue.valid())
		radiusLeftBottom = bottomLeftRadiusValue;

	const BValue& bottomRightRadiusValue = realStyle->value("border-bottom-right-radius");
	if (bottomRightRadiusValue.valid())
		radiusRightBottom = bottomRightRadiusValue;

	if (radiusLeftTop.empty() && radiusRightTop.empty() && radiusLeftBottom.empty() && radiusRightBottom.empty())
	{
		if (round)
		{
			round = 0;
		}
	}
	else
	{
		if (round.empty())
			round = new IRound();
		round->radiusLeftTop = radiusLeftTop;
		round->radiusRightTop = radiusRightTop;
		round->radiusLeftBottom = radiusLeftBottom;
		round->radiusRightBottom = radiusRightBottom;
	}
}

BHtmlItem* member_BHtmlItem::upperBlock()
{
	BHtmlItem* parent = boss->parent();
	while (parent)
	{
		if (item_member(parent)->display == BHtmlItem::Display_Block)
			return parent;
		if (item_member(parent)->display == BHtmlItem::Display_Inline_Block)
			return parent;
		parent = parent->parent();
	}
	return 0;
}
IHtmlStyle* member_BHtmlItem::baseStyle()
{
	if (html)
	{
		const BString& name = boss->name();
		IHtmlStyle* style = html_member(html)->htmlStyle->child(name);
		if (style)
			return style;
		return html_member(html)->htmlStyle;
	}
	return 0;
}
const BFont* member_BHtmlItem::baseFont()
{
	if (html)
	{
		const BString& name = boss->name();
		IHtmlStyle* style = html_member(html)->htmlStyle->child(name);
		if (style)
		{
			const BValue& fontValue = style->value("font");
			if (fontValue.valid())
				return fontValue;
		}
		const BValue& fontValue = html_member(html)->htmlStyle->value("font");
		if (fontValue.valid())
			return fontValue;
	}
	return 0;
}

BSize member_BHtmlItem::freshBlock(const BPoint& point, int page)
{
	const BString& name = boss->name();

	area = point;
	area.right() = page;
	BSize realSize = area.size();

	if (sizeRatio.width() > 0.0f)
		realSize.width() = int(realSize.width() * sizeRatio.width());
	if (sizeRatio.height() > 0.0f)
		realSize.height() = int(realSize.height() * sizeRatio.height());

	if (realSize.width() < fixedSize.width())
		realSize.setWidth(fixedSize.width());
	if (realSize.height() < fixedSize.height())
		realSize.setHeight(fixedSize.height());

	if (realSize.width() < minSize.width())
		realSize.width() = minSize.width();
	if (realSize.height() < minSize.height())
		realSize.height() = minSize.height();

	if (realSize.width() > maxSize.width())
		realSize.width() = maxSize.width();
	if (realSize.height() > maxSize.height())
		realSize.height() = maxSize.height();

	if (area.size() != realSize)
	{
		area.setSize(realSize);
	}

	rect = area;
	rect.left() += margin.left();
	rect.top() += margin.top();
	rect.right() -= margin.right();
	rect.bottom() -= margin.bottom();

	client = rect;
	client.left() += border.left();
	client.left() += padding.left();
	client.right() -= padding.right();
	client.right() -= border.right();
	client.top() += border.top();
	client.top() += padding.top();
	client.bottom() -= padding.bottom();
	client.bottom() -= border.bottom();
	page = client.right();

	BRect cursor = client.pos();
	adjustText(cursor, client.left(), client.right());
	client.expand(textRect);
	if (textAlign)
	{
		BRect alignRect = client.align(textAlign, textRect.size());
		BPoint offset = alignRect.pos() - textRect.pos();
		for (int i = 0; i < cells.size(); i++)
		{
			cells[i].move(offset);
		}
		textRect = alignRect;
		client.expand(textRect);
	}

	if (freshFunc)
	{
		(this->*freshFunc)(cursor, page);
	}
	else
	{
		BArray<BXmlNodeHolder>& children = xml_member(boss)->children;
		for (int i = 0; i < children.size(); i++)
		{
			BHtmlItem* child = children[i].as<BHtmlItem>();
			const BString& childName = child->name();
			if (item_member(child)->floatAlign)
			{
				BSize childSize = item_member(child)->freshInlineBlock(cursor.pos(), page);
				BRect childArea = rect.align(item_member(child)->floatAlign, childSize);
				BPoint offset = childArea.pos() - item_member(child)->area.pos();
				item_member(child)->moveArea(offset);
				continue;
			}
			if (item_member(child)->display == BHtmlItem::Display_Inline)
			{
				item_member(child)->freshInline(cursor, client.left(), page);
				if (childName == "br")
					linefeed(cursor);
			}
			else if (item_member(child)->display == BHtmlItem::Display_Block)
			{
				linefeed(cursor);
				BSize size = item_member(child)->freshBlock(cursor.pos(), page);
				cursor.setHeight(size.height());
				linefeed(cursor);
			}
			else if (item_member(child)->display == BHtmlItem::Display_Inline_Block)
			{
				BSize childSize = item_member(child)->freshInlineBlock(cursor.pos(), page);
				if (cursor.left() > client.left() && cursor.left() + childSize.width() > page)
				{
					linefeed(cursor);
					childSize = item_member(child)->freshInlineBlock(cursor.pos(), page);
				}
				cursor.left() += childSize.width();
				if (cursor.height() < childSize.height())
					cursor.setHeight(childSize.height());
			}
			else if (item_member(child)->display == BHtmlItem::Display_Flex)
			{
				BSize childSize = item_member(child)->freshInlineBlock(cursor.pos(), page);
				if (cursor.left() > client.left() && cursor.left() + childSize.width() > page)
				{
					linefeed(cursor);
					childSize = item_member(child)->freshInlineBlock(cursor.pos(), page);
				}
				cursor.left() += childSize.width();
				if (cursor.height() < childSize.height())
					cursor.setHeight(childSize.height());
			}
			client.expand(item_member(child)->area);
		}
	}

	if (rect.top() > client.top())
	{
		BPoint offset(0, rect.top() - client.top());
		BArray<BXmlNodeHolder>& children = xml_member(boss)->children;
		for (int i = 0; i < children.size(); i++)
		{
			BHtmlItem* child = children[i].as<BHtmlItem>();
			item_member(child)->moveArea(offset);
		}
		client += offset;
		textRect.move(offset);
		for (int i = 0; i < cells.size(); i++)
		{
			cells[i].move(offset);
		}
	}

	rect.bottom() = client.bottom();
	rect.bottom() += border.bottom();
	rect.bottom() += padding.bottom();

	area.bottom() = rect.bottom();
	area.bottom() += margin.bottom();

	if (area.right() < client.right() + padding.right() + border.right() + margin.right())
	{
		area.right() = client.right() + padding.right() + border.right() + margin.right();
		rect.right() = client.right() + padding.right() + border.right();
	}

	adjustBorder();
	return area.size();
}
BSize member_BHtmlItem::freshInlineBlock(const BPoint& point, int page)
{
	const BString& name = boss->name();

	area = point;
	if (area.right() < page)
		area.right() = page;

	int shellWidth = margin.width() + border.width() + padding.width();
	int shellHeight = margin.height() + border.height() + padding.height();
	sizeHint.set(shellWidth, shellHeight);
	if (area.width() < shellWidth)
		area.setWidth(shellWidth);
	if (area.height() < shellHeight)
		area.setHeight(shellHeight);

	rect = area;
	rect.left() += margin.left();
	rect.top() += margin.top();
	rect.right() -= margin.right();
	rect.bottom() -= margin.bottom();

	client = rect;
	client.left() += border.left();
	client.left() += padding.left();
	client.right() -= padding.right();
	client.right() -= border.right();

	client.top() += border.top();
	client.top() += padding.top();
	client.bottom() -= padding.bottom();
	client.bottom() -= border.bottom();

	page -= (border.right() + padding.right() + margin.right());
	if (client.width() < fixedSize.width())
	{
		client.setWidth(fixedSize.width());
		page = client.right();
	}
	if (client.height() < fixedSize.height())
	{
		client.setHeight(fixedSize.height());
	}

	BRect cursor = client.pos();
	adjustText(cursor, client.left(), page);
	client.expand(textRect);
	if (html->editable())
	{
		if (textRect.width() < realFont->size())
			textRect.setWidth(realFont->size());
		if (textRect.height() < realFont->size())
			textRect.setHeight(realFont->size());
		if (cursor.height() < realFont->size())
			cursor.setHeight(realFont->size());
		client.expand(textRect);
	}
	if (textAlign)
	{
		BRect alignRect = client.align(textAlign, textRect.size());
		BPoint offset = alignRect.pos() - textRect.pos();
		for (int i = 0; i < cells.size(); i++)
		{
			cells[i].move(offset);
		}
		textRect = alignRect;
		client.expand(textRect);
	}

	if (freshFunc)
	{
		(this->*freshFunc)(cursor, page);
	}
	else
	{
		BArray<BXmlNodeHolder>& children = xml_member(boss)->children;
		for (int i = 0; i < children.size(); i++)
		{
			BHtmlItem* child = children[i].as<BHtmlItem>();
			const BString& childName = child->name();
			if (item_member(child)->display == BHtmlItem::Display_Inline)
			{
				item_member(child)->freshInline(cursor, client.left(), page);
				if (childName == "br")
					linefeed(cursor);
			}
			else if (item_member(child)->display == BHtmlItem::Display_Block)
			{
				linefeed(cursor);
				BSize size = item_member(child)->freshBlock(cursor.pos(), page);
				BSize hsize = item_member(child)->sizeHint;
				if (sizeHint.width() < hsize.width())
					sizeHint.width() = hsize.width();
				cursor.setHeight(size.height());
				linefeed(cursor);
			}
			else if (item_member(child)->display == BHtmlItem::Display_Inline_Block)
			{
				BSize childSize = item_member(child)->freshInlineBlock(cursor.pos(), page);
				if (cursor.height() < childSize.height())
					cursor.setHeight(childSize.height());
				if (cursor.x() + childSize.width() > page && page > 0)
				{
					linefeed(cursor);
					childSize = item_member(child)->freshInlineBlock(cursor.pos(), page);
				}
				cursor.left() += childSize.width();
			}
			else if (item_member(child)->display == BHtmlItem::Display_Flex)
			{
				BSize childSize = item_member(child)->freshInlineBlock(cursor.pos(), page);
				if (cursor.height() < childSize.height())
					cursor.setHeight(childSize.height());
				if (cursor.x() + childSize.width() > page)
				{
					linefeed(cursor);
					childSize = item_member(child)->freshInlineBlock(cursor.pos(), page);
				}
				cursor.left() += childSize.width();
			}
			client.expand(item_member(child)->area);
		}
	}

	if (rect.right() < client.right())
	{
		rect.right() = client.right();
		rect.right() += border.right();
		rect.right() += padding.right();
	}

	rect.bottom() = client.bottom() + border.bottom() + padding.bottom();
	area.right() = rect.right() + margin.right();
	area.bottom() = rect.bottom() + margin.bottom();

	if (area.right() < client.right() + padding.right() + border.right() + margin.right())
	{
		area.right() = client.right() + padding.right() + border.right() + margin.right();
		rect.right() = client.right() + padding.right() + border.right();
	}

	adjustBorder();
	return area.size();
}
void member_BHtmlItem::freshInline(BRect& cursor, int left, int page)
{
	const BString& name = boss->name();
	if (name == "sub")
		cursor.move(0, realFont->size() / 2);
	if (name == "sup")
		cursor.move(0, -realFont->size() / 2);

	cursor.right() = cursor.left();
	area = cursor;

	rect = area;
	rect.left() += margin.left();
	rect.top() += margin.top();

	client = rect;
	client.left() += border.left();
	client.left() += padding.left();
	client.top() += border.top();
	client.top() += padding.top();

	cursor.setPos(client.pos());
	adjustText(cursor, left, page);
	client.expand(textRect);
	if (html->editable())
	{
		if (textRect.width() < realFont->size())
			textRect.setWidth(realFont->size());
		if (textRect.height() < realFont->size())
			textRect.setHeight(realFont->size());
		if (cursor.height() < realFont->size())
			cursor.setHeight(realFont->size());
		client.expand(textRect);
	}
	if (textAlign)
	{
		BRect alignRect = client.align(textAlign, textRect.size());
		BPoint offset = alignRect.pos() - textRect.pos();
		for (int i = 0; i < cells.size(); i++)
		{
			cells[i].move(offset);
		}
		textRect = alignRect;
		client.expand(textRect);
	}

	if (freshFunc)
	{
		(this->*freshFunc)(cursor, page);
	}
	else
	{
		BArray<BXmlNodeHolder>& children = xml_member(boss)->children;
		for (int i = 0; i < children.size(); i++)
		{
			BHtmlItem* child = children[i].as<BHtmlItem>();
			const BString& childName = child->name();
			if (item_member(child)->display == BHtmlItem::Display_Inline)
			{
				item_member(child)->freshInline(cursor, left, page);
				if (childName == "br")
					linefeed(cursor);
			}
			else if (item_member(child)->display == BHtmlItem::Display_Block)
			{
				linefeed(cursor);
				BSize size = item_member(child)->freshBlock(cursor.pos(), page);
				cursor.setHeight(size.height());
				linefeed(cursor);
			}
			else if (item_member(child)->display == BHtmlItem::Display_Inline_Block)
			{
				BSize childSize = item_member(child)->freshInlineBlock(cursor.pos(), page);
				if (cursor.x() + childSize.width() > page && page > 0)
				{
					linefeed(cursor);
					childSize = item_member(child)->freshInlineBlock(cursor.pos(), page);
				}
				cursor.left() += childSize.width();
			}
			else if (item_member(child)->display == BHtmlItem::Display_Flex)
			{
				BSize childSize = item_member(child)->freshInlineBlock(cursor.pos(), page);
				if (cursor.x() + childSize.width() > page)
				{
					linefeed(cursor);
					childSize = item_member(child)->freshInlineBlock(cursor.pos(), page);
				}
				cursor.left() += childSize.width();
			}
			client.expand(item_member(child)->area);
		}
	}

	rect = client;
	rect.left() -= padding.left();
	rect.left() -= border.left();

	rect.top() -= padding.top();
	rect.top() -= border.top();

	rect.right() += border.right();
	rect.right() += padding.right();

	rect.bottom() += border.bottom();
	rect.bottom() += padding.bottom();

	area = rect;
	area.left() -= margin.left();
	area.top() -= margin.top();
	area.right() += margin.right();
	area.bottom() += margin.bottom();

	adjustBorder();

	if (name == "sub")
	{
		cursor.move(0, -realFont->size() / 2);
	}
	if (name == "sup")
	{
		cursor.move(0, realFont->size() / 2);
	}
}

void member_BHtmlItem::linefeed(BRect& cursor)
{
	cursor.left() = client.left();
	cursor.top() = cursor.bottom() + spacing;
}
void member_BHtmlItem::fresh_table(BRect& cursor, int page)
{
	BArray<BXmlNodeHolder>& children = xml_member(boss)->children;

	BIntArray columnWidths;
	BFloatArray columnWeights;
	for (int r = 0; r < children.size(); r++)
	{
		BHtmlItem* rowItem = children[r].as<BHtmlItem>();
		if (columnWidths.size() < rowItem->childCount())
		{
			columnWidths.resize(rowItem->childCount());
			columnWeights.resize(rowItem->childCount());
		}
	}
	for (int r = 0; r < children.size(); r++)
	{
		BHtmlItem* rowItem = children[r].as<BHtmlItem>();
		linefeed(cursor);
		item_member(rowItem)->freshInline(cursor, client.left(), 0);
		linefeed(cursor);
		for (int c = 0; c < rowItem->childCount(); c++)
		{
			BHtmlItem* item = rowItem->child(c);
			BSize size = item_member(item)->area.size();
			if (columnWidths[c] < size.width())
				columnWidths[c] = size.width();
			columnWeights[c] += size.width();
		}
	}

	if (columnWidths.size() && fixedSize.width() < 1)
	{
		float totalWeight = 0;
		for (int c = 0; c < columnWidths.size(); c++)
		{
			columnWeights[c] /= children.size();
			totalWeight += columnWeights[c];
			sizeHint.width() += columnWidths[c] + spacing;
		}
		sizeHint.width() -= spacing;
		if (page > 0)
			page -= spacing * (columnWidths.size() - 1);
		if (page > 0 && sizeHint.width() > page)
		{
			for (int c = 0; c < columnWidths.size(); c++)
			{
				columnWidths[c] = int((columnWeights[c] / totalWeight) * page);
			}
			client.setWidth(page);
		}
	}

	if (page <= 0 && client.width() < sizeHint.width())
	{
		client.setWidth(sizeHint.width());
	}

	int y = client.bottom();
	for (int r = 0; r < children.size(); r++)
	{
		BHtmlItem* rowItem = children[r].as<BHtmlItem>();
		int x = client.left();
		int rowHeight = item_member(rowItem)->realFont->size();
		for (int c = 0; c < rowItem->childCount(); c++)
		{
			BHtmlItem* item = rowItem->child(c);
			int columnWidth = columnWidths[c];
			item_member(item)->freshBlock(BPoint(x, y), x + columnWidth);
			BSize size = item_member(item)->area.size();
			x += spacing;
			x += columnWidth;
			if (rowHeight < size.height())
				rowHeight = size.height();
		}
		for (int c = 0; c < rowItem->childCount(); c++)
		{
			BHtmlItem* item = rowItem->child(c);
			if (item_member(item)->area.height() < rowHeight)
			{
				item_member(item)->area.setHeight(rowHeight);
				item_member(item)->adjustRects();
			}
		}
		item_member(rowItem)->area.set(client.left(), y, client.width(), rowHeight);
		item_member(rowItem)->adjustRects();
		y += spacing;
		y += rowHeight;
	}
	if (client.bottom() < y)
	{
		client.bottom() = y;
	}
}
void member_BHtmlItem::fresh_ulist(BRect& cursor, int page)
{
	BArray<BXmlNodeHolder>& children = xml_member(boss)->children;
	int top = client.bottom();
	int left = client.left() + realFont->size();
	int rowWidth = 0;
	if (children.size())
	{
		for (int i = 0; i < children.size(); i++)
		{
			BHtmlItem* item = boss->child(i);
			BSize itemSize = item_member(item)->freshInlineBlock(BPoint(left, client.bottom()), page);
			if (client.right() < item_member(item)->area.right())
				client.right() = item_member(item)->area.right();
			if (rowWidth < itemSize.width())
				rowWidth = itemSize.width();
			client.bottom() += spacing;
			client.bottom() += itemSize.height();
		}
		client.bottom() -= spacing;
	}
	for (int i = 0; i < children.size(); i++)
	{
		BHtmlItem* item = boss->child(i);
		BSize itemSize = item_member(item)->freshInlineBlock(BPoint(left, top), client.right());
		if (itemSize.width() < rowWidth)
		{
			item_member(item)->area.setWidth(rowWidth);
			item_member(item)->adjustRects();
		}
		top += spacing;
		top += itemSize.height();
	}
}
void member_BHtmlItem::fresh_olist(BRect& cursor, int page)
{
	BArray<BXmlNodeHolder>& children = xml_member(boss)->children;
	int top = client.bottom();
	int left = client.left() + realFont->size();
	int rowWidth = 0;
	if (children.size())
	{
		for (int i = 0; i < children.size(); i++)
		{
			BHtmlItem* item = boss->child(i);
			BSize itemSize = item_member(item)->freshInlineBlock(BPoint(left, client.bottom()), page);
			if (client.right() < item_member(item)->area.right())
				client.right() = item_member(item)->area.right();
			if (rowWidth < itemSize.width())
				rowWidth = itemSize.width();
			client.bottom() += spacing;
			client.bottom() += itemSize.height();
		}
		client.bottom() -= spacing;
	}
	for (int i = 0; i < children.size(); i++)
	{
		BHtmlItem* item = boss->child(i);
		BSize itemSize = item_member(item)->freshInlineBlock(BPoint(left, top), client.right());
		if (itemSize.width() < rowWidth)
		{
			item_member(item)->area.setWidth(rowWidth);
			item_member(item)->adjustRects();
		}
		top += spacing;
		top += itemSize.height();
	}
}
void member_BHtmlItem::fresh_dlist(BRect& cursor, int page)
{
	BArray<BXmlNodeHolder>& children = xml_member(boss)->children;
	client.setHeight(rect.height());
	int left = client.left() + realFont->size();
	for (int i = 0; i < children.size(); i++)
	{
		BHtmlItem* item = boss->child(i);
		BSize itemSize = item_member(item)->freshBlock(cursor.pos(), page);
		item_member(item)->area.set(left, client.bottom(), itemSize.width(), itemSize.height());
		if (client.width() < itemSize.width())
			client.setWidth(itemSize.width());
		client.bottom() += spacing;
		client.bottom() += itemSize.height();
	}
}
void member_BHtmlItem::fresh_image(BRect& cursor, int page)
{
	if (image)
	{
		if (fixedSize.width() < 1)
			client.setWidth(image->width());
		if (fixedSize.height() < 1)
			client.setHeight(image->height());
		cursor.move(client.width(), 0);
		if (cursor.height() < client.height())
			cursor.setHeight(client.height());
		if (cursor.x() > page && client.width() < page)
		{
			linefeed(cursor);
		}
	}
}

void member_BHtmlItem::moveArea(const BPoint& offset)
{
	const BString& name = boss->name();
	area.move(offset);
	adjustRects();
	textRect.move(offset);
	for (int i = 0; i < cells.size(); i++)
	{
		cells[i].move(offset);
	}
	BArray<BXmlNodeHolder>& children = xml_member(boss)->children;
	for (int i = 0; i < children.size(); i++)
	{
		BHtmlItem* child = children[i].as<BHtmlItem>();
		if (item_member(child)->display == BHtmlItem::Display_Block)
			item_member(child)->area.setWidth(client.width());
		item_member(child)->moveArea(offset);
	}
}
void member_BHtmlItem::adjustRects()
{
	rect = area;
	rect.left() += margin.left();
	rect.top() += margin.top();
	rect.right() -= margin.right();
	rect.bottom() -= margin.bottom();

	client = rect;
	client.left() += border.left();
	client.left() += padding.left();
	client.top() += border.top();
	client.top() += padding.top();
	client.right() -= border.right();
	client.right() -= padding.right();
	client.bottom() -= border.bottom();
	client.bottom() -= padding.bottom();

	adjustBorder();
}
void member_BHtmlItem::adjustBorder()
{
	rectLeft.reset();
	rectTop.reset();
	rectRight.reset();
	rectBottom.reset();
	if (border.left() > 0)
		rectLeft.set(rect.x(), rect.y(), border.left(), rect.height());
	if (border.top() > 0)
		rectTop.set(rect.x() + border.left(), rect.y(), rect.width() - border.left() - border.right(), border.top());
	if (border.right() > 0)
		rectRight.set(rect.right() - border.right(), rect.top(), border.right(), rect.height());
	if (border.bottom() > 0)
		rectBottom.set(rect.x() + border.left(), rect.bottom() - border.bottom(), rect.width() - border.left() - border.right(), border.bottom());

	if (round)
	{
		float width = (float)rect.width();
		float height = (float)rect.height();
		round->frameSize.set(width, height);
		round->centerLeftTop.set(round->radiusLeftTop.width(), round->radiusLeftTop.height());
		round->centerRightTop.set(width - round->radiusRightTop.width(), round->radiusRightTop.height());
		round->centerLeftBottom.set(round->radiusLeftBottom.width(), height - round->radiusLeftBottom.height());
		round->centerRightBottom.set(width - round->radiusRightBottom.width(), height - round->radiusRightBottom.height());

		BPointfArray outerPoints;
		BPointfArray innerPoints;

		BRectf outerRect(0, 0, width, height);
		round->createPoints(outerRect, outerPoints);

		BRectf innerRect = outerRect;
		innerRect.left() += border.left();
		innerRect.top() += border.top();
		innerRect.right() -= border.right();
		innerRect.bottom() -= border.bottom();

		round->createPoints(innerRect, innerPoints);
		round->createTriangles(outerPoints, innerPoints, round->borderPoints);

		round->backgroundPoints = innerPoints;
		round->backgroundPoints.prepend(innerRect.center());

		round->movePoints(rect.pos());
	}

}
void member_BHtmlItem::adjustFloat()
{
	BArray<BXmlNodeHolder>& children = xml_member(boss)->children;
	if (floatAlign)
	{
		if (BHtmlItem* block = upperBlock())
		{
			BRect newArea = item_member(block)->client.align(floatAlign, area.size());
			BPoint offset = newArea.pos() - area.pos();
			this->moveArea(offset);
		}
	}
	for (int r = 0; r < children.size(); r++)
	{
		BHtmlItem* child = children[r].as<BHtmlItem>();
		item_member(child)->adjustFloat();
	}
}

void member_BHtmlItem::paint(BPainter& painter, const BPoint& offset)
{
	if (display == BHtmlItem::Display_Block || display == BHtmlItem::Display_Inline_Block)
	{
		State state = painter.state();
		if (!painter.setColor(background))
			painter.setColor(Color_Background, state);
		if (round)
		{
			painter.setPolySmooth(true);
			glBegin(GL_TRIANGLE_FAN);
			for (int i = 0; i < round->backgroundPoints.size(); i++)
			{
				BPointf point = round->backgroundPoints[i] + offset;
				glVertex2f(point.x(), point.y());
			}
			glEnd();
			painter.setPolySmooth(false);
		}
		else
		{
			painter.fillRect(rect + offset);
		}

		if (backgroundImage)
		{
			BRect imageRect = client + offset;
			if (imageRect.height() > backgroundImage->height())
				imageRect.setHeight(backgroundImage->height());
			painter.drawImage(imageRect, backgroundImage);
		}
		if (round)
		{
			if (painter.setColor(borderLeftColor))
			{
				painter.setPolySmooth(true);
				glBegin(GL_TRIANGLES);
				for (int i = 0; i < round->borderPoints.size(); i++)
				{
					BPointf point = round->borderPoints[i] + offset;
					glVertex2f(point.x(), point.y());
				}
				glEnd();
				painter.setPolySmooth(false);
			}
		}
		else
		{
			if (border.left() && painter.setColor(borderLeftColor))
				painter.fillRect(rectLeft + offset);
			if (border.top() && painter.setColor(borderTopColor))
				painter.fillRect(rectTop + offset);
			if (border.right() && painter.setColor(borderRightColor))
				painter.fillRect(rectRight + offset);
			if (border.bottom() && painter.setColor(borderBottomColor))
				painter.fillRect(rectBottom + offset);
		}
	}
	if (cells.size())
	{
		if (inputIndex >= 0 && inputCount > 0)
		{
			painter.setColor(Color_Selection);
			int begin = bMin(inputIndex, inputIndex + inputCount);
			int end = bMax(inputIndex, inputIndex + inputCount);
			for (int i = begin; i < end; i++)
			{
				const ICodeCell& cell = cells[i];
				painter.fillRect(cell.rect + offset);
			}
		}
		if (textColor.a())
			painter.setColor(textColor);
		else
			painter.setColor(Color_Text);
		paintText(painter.clip(), offset);
	}

	if (paintFunc)
	{
		(this->*paintFunc)(painter, offset);
	}
}
void member_BHtmlItem::paint_ulist(BPainter& painter, const BPoint& offset)
{
	if (textColor.a())
		painter.setColor(textColor);
	else
		painter.setColor(Color_Text);
	BArray<BXmlNodeHolder>& children = xml_member(boss)->children;
	for (int i = 0; i < children.size(); i++)
	{
		BHtmlItem* item = boss->child(i);
		BRect iconRect = item_member(item)->area;
		iconRect.setWidth(0);
		iconRect.left() = client.left();
		iconRect += offset;
		if (listStyle)
		{
			if (BImage* image = item_member(item)->listStyleImage)
			{
				BRect imageRect = iconRect.align(Align_Center, image->size());
				painter.drawImage(imageRect, image);
			}
			else if (listStyle == BHtmlItem::Style_Disc)
			{
				BRect graphRect = iconRect % 50;
				painter.drawGraph(graphRect, Graph_Circle_Fill);
			}
		}
	}
}
void member_BHtmlItem::paint_olist(BPainter& painter, const BPoint& offset)
{
	if (textColor.a())
		painter.setColor(textColor);
	else
		painter.setColor(Color_Text);
	BArray<BXmlNodeHolder>& children = xml_member(boss)->children;
	for (int i = 0; i < children.size(); i++)
	{
		BHtmlItem* item = boss->child(i);
		BRect numRect = item_member(item)->area;
		numRect.setWidth(0);
		numRect.left() = client.left();
		numRect += offset;
		painter.drawText(numRect, BString(i), Align_Center);
	}
}
void member_BHtmlItem::paint_dlist(BPainter& painter, const BPoint& offset)
{

}
void member_BHtmlItem::paint_image(BPainter& painter, const BPoint& offset)
{
	painter.drawImage(client + offset, image);
}

void member_BHtmlItem::flushText()
{
	xml_member(boss)->text.reset();
	for (int i = 0; i < cells.size(); i++)
	{
		xml_member(boss)->text.append(cells[i].code);
	}
}
void member_BHtmlItem::resetCells()
{
	cells.clear();
	for (auto it = xml_member(boss)->text.begin(); it < xml_member(boss)->text.end(); it++)
	{
		cells.append(ICodeCell());
		ICodeCell& cell = cells.last();
		cell.code = *it;
	}
	inputIndex = 0;
	inputCount = 0;
	dirty = true;
}

bool member_BHtmlItem::remove(int pos, int count)
{
	if (!cells.remove(pos, count))
		return false;
	dirty = true;
	flushText();
	return true;
}
void member_BHtmlItem::append(const BString& text)
{
	xml_member(boss)->text << text;
	for (auto cit = text.begin(); cit.valid(); ++cit)
	{
		ICodeCell& cell = cells.append();
		cell.code = *cit;
	}
	dirty = true;
}
bool member_BHtmlItem::insert(const BCode& code)
{
	if (!cells.insert(inputIndex, ICodeCell()))
		return false;
	ICodeCell& cell = cells[inputIndex];
	cell.code = code;
	inputIndex++;
	dirty = true;
	flushText();
	return true;
}
bool member_BHtmlItem::insert(const BString& text)
{
	int count = text.count();
	if (!cells.insert(inputIndex, ICodeCell(), count))
		return false;
	for (auto it = text.begin(); it.valid(); it++)
	{
		ICodeCell& cell = cells[inputIndex++];
		cell.code = *it;
	}
	dirty = true;
	flushText();
	return true;
}

void member_BHtmlItem::substr(BString& str, int pos, int count) const
{
	if (pos < 0 || pos >= cells.size())
		return;
	if (count < 0)
	{
		pos += count;
		count = -count;
	}
	int end = pos + count;
	if (end > cells.size())
	{
		count = cells.size() - pos;
		end = cells.size();
	}
	for (int i = pos; i < end; i++)
	{
		str.append(cells[i].code);
	}
}
void member_BHtmlItem::selection(BString& str) const
{
	if (inputIndex < 0 || inputIndex >= cells.size())
		return;
	if (inputCount < 1)
		return;
	int stop = inputIndex + inputCount;
	if (stop > cells.size())
		return;
	for (int i = inputIndex; i < stop; i++)
	{
		str.append(cells[i].code);
	}
}

bool member_BHtmlItem::selectWord()
{
	if (cells.empty())
		return false;

	if (!cells.check(inputIndex))
		return false;

	bool large = cells[inputIndex].code.isLarge();
	bool space = cells[inputIndex].code.isSpace();
	bool punct = cells[inputIndex].code.isPunct();
	bool alpha = cells[inputIndex].code.isAlpha();

	int begin = inputIndex;
	while (begin >= 0)
	{
		const BCode& code = cells[begin].code;
		if (large != code.isLarge())
			break;
		if (space != code.isSpace())
			break;
		if (punct != code.isPunct())
			break;
		if (alpha != code.isAlpha())
			break;
		begin--;
	}
	begin++;
	int end = begin;
	while (end < cells.size())
	{
		const BCode& code = cells[end].code;
		if (large != code.isLarge())
			break;
		if (space != code.isSpace())
			break;
		if (punct != code.isPunct())
			break;
		if (alpha != code.isAlpha())
			break;
		end++;
	}
	inputIndex = begin;
	inputCount = end - begin;
	return inputCount > 0;
}
bool member_BHtmlItem::removeSelection()
{
	if (inputIndex >= 0 && inputCount > 0)
	{
		cells.remove(inputIndex, inputCount);
		inputCount = 0;
		dirty = true;
		flushText();
		return true;
	}
	return false;
}

bool member_BHtmlItem::seekHead()
{
	if (inputIndex != 0 || inputCount != 0)
	{
		inputIndex = 0;
		inputCount = 0;
		return true;
	}
	return false;
}
bool member_BHtmlItem::seekPoint(const BPoint& point)
{
	int index = indexAt(point);
	if (inputIndex != index || inputCount != 0)
	{
		inputIndex = index;
		inputCount = 0;
		return true;
	}
	return false;
}
bool member_BHtmlItem::seekTail()
{
	int tail = cells.size();
	if (cells.last().code == '\n')
		tail = cells.size() - 1;
	if (inputIndex != tail || inputCount != 0)
	{
		inputIndex = tail;
		inputCount = 0;
		return true;
	}
	return false;
}

int member_BHtmlItem::seekPrevCode()
{
	inputIndex--;
	inputCount = 0;
	return inputIndex;
}
int member_BHtmlItem::seekNextCode()
{
	if (inputIndex < cells.size())
	{
		inputIndex++;
		inputCount = 0;
	}
	return inputIndex;
}
int member_BHtmlItem::seekPrevWord()
{
	inputCount = 0;
	bool found = false;
	int begin = inputIndex;
	while (--begin > 0)
	{
		const BCode& code = cells[begin].code;
		if (code > 255 || !code.isSpace())
		{
			found = true;
			continue;
		}
		if (found && code.isSpace())
		{
			begin++;
			break;
		}
	}
	inputIndex = begin;
	return inputIndex;
}
int member_BHtmlItem::seekNextWord()
{
	int size = cells.size();
	int begin = inputIndex;
	while (begin < size)
	{
		const BCode& code = cells[begin].code;
		if (code < 255)
		{
			if (code.isSpace())
				break;
		}
		begin++;
	}
	while (begin < size)
	{
		const BCode& code = cells[begin].code;
		if (code < 255)
		{
			if (!code.isSpace())
				break;
		}
		begin++;
	}
	inputIndex = begin;
	return inputIndex;
}

int member_BHtmlItem::indexAt(const BPoint& pos) const
{
	const BString& name = boss->name();
	if (pos.y() < textRect.top())
		return -1;
	if (pos.y() > textRect.bottom())
		return cells.size();
	int index = 0;
	while (index < cells.size())
	{
		const BRect& rect = cells[index].rect;
		if (rect.bottom() < pos.y())
		{
			index++;
			continue;
		}
		if (rect.top() > pos.y())
		{
			index--;
			break;
		}
		if (rect.center().x() > pos.x())
			break;
		index++;
	}
	return index;
}
int member_BHtmlItem::wordWidth(int index) const
{
	if (index > 0 && (!cells[index - 1].code.isSpace() && !cells[index - 1].code.isPunct()))
		return 0;
	if (cells[index].code.isSpace())
		return cells[index].rect.width();
	int width = cells[index].rect.width();
	const BCode& code = cells[index].code;
	const char* ustr = code.str();
	if (code.isSpace())
		return width;
	if (code.isPunct())
		return width;
	if (code.num() > 1)
		return width;
	while (++index < cells.size())
	{
		const BCode& code = cells[index].code;
		if (code.isSpace())
			break;
		if (code.isPunct())
			break;
		if (code.num() > 1)
			break;
		width += cells[index].rect.width();
	}
	return width;
}

void member_BHtmlItem::adjustText(BRect& cursor, int left, int page)
{
	textRect = cursor;
	if (cells.empty())
	{
		if (html->editable())
		{
			if (textRect.width() < realFont->size())
				textRect.setWidth(realFont->size());
			if (textRect.height() < realFont->size())
				textRect.setHeight(realFont->size());
			cursor.move(realFont->size(), 0);
		}
		return;
	}

	if (boss->name() == "pre")
		page = 0;

	for (int i = 0; i < cells.size(); i++)
	{
		ICodeCell& cell = cells[i];
		cell.fresh(realFont);
	}

	int rowHeight = bMax(int(realFont->size() * lineHeight), cursor.height());
	int up = (int)(rowHeight * 0.2f);
	for (int i = 0; i < cells.size(); i++)
	{
		ICodeCell& cell = cells[i];
		if (page > 0)
		{
			if (wordWrap)
			{
				int ww = wordWidth(i);
				if (ww < page && cursor.left() + ww > page)
				{
					cursor.left() = left;
					cursor.top() = cursor.bottom();
					rowHeight = realFont->size();
					up = (int)(rowHeight * 0.2f);
				}
			}
			else
			{
				int cw = cell.rect.width();
				if (cw < page && cursor.left() + cw > page)
				{
					cursor.left() = left;
					cursor.top() = cursor.bottom();
					rowHeight = realFont->size();
					up = (int)(rowHeight * 0.2f);
				}
			}
		}
		cell.rect.setPos(cursor.pos());
		cell.rect.setHeight(rowHeight);
		cell.image_rect.setPos(cursor.left() + cell.image_left, cell.rect.bottom() - cell.image_top - up);
		cursor.left() += cell.rect.width();
		if (cursor.height() < rowHeight)
			cursor.setHeight(rowHeight);
		if (cell.code == '\n')
		{
			cursor.left() = left;
			cursor.top() = cursor.bottom();
			cursor.bottom() += rowHeight;
			rowHeight = realFont->size();
			up = (int)(rowHeight * 0.2f);
		}
		textRect.expand(cell.rect);
	}
}
void member_BHtmlItem::paintText(const BRect& clip, const BPoint& offset)
{
	int bottom = clip.bottom();
	glEnable(GL_TEXTURE_2D);
	for (int i = 0; i < cells.size(); i++)
	{
		const ICodeCell& cell = cells[i];
		BRect rect = cell.image_rect + offset;
		if (rect.top() > bottom)
			break;
		if (cell.image.empty())
			continue;
		int x = rect.x();
		int y = rect.y();
		int w = rect.width();
		int h = rect.height();
		if (texture_member(cell.image)->dirty)
			cell.image->flush();
		glBindTexture(GL_TEXTURE_2D, texture_member(cell.image)->tbo);
		glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 0.0f);		glVertex2i(x, y);
		glTexCoord2f(0.0f, 1.0f);		glVertex2i(x, y + h);
		glTexCoord2f(1.0f, 1.0f);		glVertex2i(x + w, y + h);
		glTexCoord2f(1.0f, 0.0f);		glVertex2i(x + w, y);
		glEnd();
		glBindTexture(GL_TEXTURE_2D, 0);
	}
	glDisable(GL_TEXTURE_2D);
}
